package simpledb.systemtest;

import simpledb.common.Database;
import simpledb.common.DbException;
import simpledb.common.Utility;
import simpledb.execution.Predicate;
import org.junit.Test;
import simpledb.storage.HeapFile;
import simpledb.storage.IntField;
import simpledb.transaction.TransactionAbortedException;
import simpledb.transaction.TransactionId;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;

/**
 * Filter 操作的基础系统级测试类。
 *
 * 提供通用的测试逻辑，用于验证 Predicate 是否能正确过滤表中的数据。
 * 子类需要实现 applyPredicate 方法来执行具体的过滤操作。
 */
public abstract class FilterBase extends SimpleDbTestBase {

    // 表列数（每张表默认3列）
    private static final int COLUMNS = 3;

    // 表行数（每张表默认1097行）
    private static final int ROWS    = 1097;

    /**
     * 应用给定的谓词（Predicate）到指定的表上，并返回匹配的元组数量。
     *
     * @param table 要操作的 HeapFile 表文件
     * @param tid 当前事务 ID
     * @param predicate 过滤条件
     * @return 匹配的元组数量
     * @throws DbException 数据库异常
     * @throws TransactionAbortedException 事务中止异常
     */
    protected abstract int applyPredicate(HeapFile table, TransactionId tid, Predicate predicate) throws DbException,
                                                                                                 TransactionAbortedException;

    /**
     * 可选方法：在 applyPredicate 执行后验证数据库状态。
     * 默认无操作，子类可重写此方法进行额外验证。
     */
    protected void validateAfter(HeapFile table) throws DbException, TransactionAbortedException, IOException {
    }

    // 保存创建的元组，用于调试或后续验证
    protected List<List<Integer>> createdTuples;

    /**
     * 在事务中运行 applyPredicate 并返回结果。
     */
    private int runTransactionForPredicate(HeapFile table, Predicate predicate) throws IOException, DbException,
                                                                               TransactionAbortedException {
        TransactionId tid = new TransactionId();
        System.out.println("【runTransactionForPredicate】开始事务：" + tid);

        int result = applyPredicate(table, tid, predicate);
        System.out.println("【runTransactionForPredicate】匹配记录数：" + result);

        Database.getBufferPool().transactionComplete(tid);
        System.out.println("【runTransactionForPredicate】事务完成");

        return result;
    }

    /**
     * 验证谓词是否按预期工作。
     *
     * @param column 要测试的列索引
     * @param columnValue 列的固定值（所有行都设置为该值）
     * @param trueValue 期望匹配的值
     * @param falseValue 期望不匹配的值
     * @param operation 谓词操作符（如 EQUALS、LESS_THAN 等）
     */
    private void validatePredicate(int column, int columnValue, int trueValue, int falseValue, Predicate.Op operation)
                                                                                                                      throws IOException,
                                                                                                                      DbException,
                                                                                                                      TransactionAbortedException {

        System.out.println("【validatePredicate】开始测试谓词操作：" + operation);
        System.out.println("【validatePredicate】列号：" + column + "，列值：" + columnValue);
        System.out.println("【validatePredicate】trueValue：" + trueValue + "，falseValue：" + falseValue);

        // 测试 true 值
        System.out.println("【validatePredicate】测试匹配值：" + trueValue);
        HeapFile f = createTable(column, columnValue);
        Predicate predicate = new Predicate(column, operation, new IntField(trueValue));
        assertEquals(ROWS, runTransactionForPredicate(f, predicate));
        f = Utility.openHeapFile(COLUMNS, f.getFile());
        validateAfter(f);
        System.out.println("【validatePredicate】匹配值测试通过");

        // 测试 false 值
        System.out.println("【validatePredicate】测试不匹配值：" + falseValue);
        f = createTable(column, columnValue);
        predicate = new Predicate(column, operation, new IntField(falseValue));
        assertEquals(0, runTransactionForPredicate(f, predicate));
        f = Utility.openHeapFile(COLUMNS, f.getFile());
        validateAfter(f);
        System.out.println("【validatePredicate】不匹配值测试通过");
        System.out.println("【validatePredicate】谓词操作测试完成 ✅\n");
    }

    /**
     * 创建一张随机表，其中某一列具有固定值。
     *
     * @param column 要设置固定值的列索引
     * @param columnValue 固定值
     * @return 创建的 HeapFile 对象
     */
    private HeapFile createTable(int column, int columnValue) throws IOException {
        Map<Integer, Integer> columnSpecification = new HashMap<>();
        columnSpecification.put(column, columnValue);
        createdTuples = new ArrayList<>();

        System.out.println("【createTable】正在创建表...");
        System.out.println("【createTable】列：" + column + " 设置值为：" + columnValue);
        System.out.println("【createTable】总行数：" + ROWS);

        return SystemTestUtil.createRandomHeapFile(COLUMNS, ROWS, columnSpecification, createdTuples);
    }

    // ================== 测试方法 ==================

    /**
     * 测试 EQUALS 操作符：
     * - 列 0 的值是 1；
     * - 匹配值 1（应全部匹配）；
     * - 不匹配值 2（应无匹配）；
     */
    @Test
    public void testEquals() throws DbException, TransactionAbortedException, IOException {
        System.out.println("【testEquals】开始测试 EQUALS 操作符");
        validatePredicate(0, 1, 1, 2, Predicate.Op.EQUALS);
        System.out.println("【testEquals】EQUALS 操作符测试完成 ✅\n");
    }

    /**
     * 测试 LESS_THAN 操作符：
     * - 列 1 的值是 1；
     * - 匹配值 2（1 < 2 → 全部匹配）；
     * - 不匹配值 1（1 < 1 → 不匹配）；
     */
    @Test
    public void testLessThan() throws DbException, TransactionAbortedException, IOException {
        System.out.println("【testLessThan】开始测试 LESS_THAN 操作符");
        validatePredicate(1, 1, 2, 1, Predicate.Op.LESS_THAN);
        System.out.println("【testLessThan】LESS_THAN 操作符测试完成 ✅\n");
    }

    /**
     * 测试 LESS_THAN_OR_EQ 操作符：
     * - 列 2 的值是 42；
     * - 匹配值 42（42 <= 42 → 全部匹配）；
     * - 不匹配值 41（42 <= 41 → 不成立）；
     */
    @Test
    public void testLessThanOrEq() throws DbException, TransactionAbortedException, IOException {
        System.out.println("【testLessThanOrEq】开始测试 LESS_THAN_OR_EQ 操作符");
        validatePredicate(2, 42, 42, 41, Predicate.Op.LESS_THAN_OR_EQ);
        System.out.println("【testLessThanOrEq】LESS_THAN_OR_EQ 操作符测试完成 ✅\n");
    }

    /**
     * 测试 GREATER_THAN 操作符：
     * - 列 2 的值是 42；
     * - 匹配值 41（42 > 41 → 全部匹配）；
     * - 不匹配值 42（42 > 42 → 不成立）；
     */
    @Test
    public void testGreaterThan() throws DbException, TransactionAbortedException, IOException {
        System.out.println("【testGreaterThan】开始测试 GREATER_THAN 操作符");
        validatePredicate(2, 42, 41, 42, Predicate.Op.GREATER_THAN);
        System.out.println("【testGreaterThan】GREATER_THAN 操作符测试完成 ✅\n");
    }

    /**
     * 测试 GREATER_THAN_OR_EQ 操作符：
     * - 列 2 的值是 42；
     * - 匹配值 42（42 >= 42 → 全部匹配）；
     * - 不匹配值 43（42 >= 43 → 不成立）；
     */
    @Test
    public void testGreaterThanOrEq() throws DbException, TransactionAbortedException, IOException {
        System.out.println("【testGreaterThanOrEq】开始测试 GREATER_THAN_OR_EQ 操作符");
        validatePredicate(2, 42, 42, 43, Predicate.Op.GREATER_THAN_OR_EQ);
        System.out.println("【testGreaterThanOrEq】GREATER_THAN_OR_EQ 操作符测试完成 ✅\n");
    }
}
